function [varargout] = BVQXfile(varargin)
% BVQXfile (Object Class)
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Version:  v0.8a
% Build:    9102301
% Date:     Oct-23 2009, 1:11 AM CEST
% Author:   Jochen Weber, SCAN Unit, Columbia University, NYC, NY, USA
% URL/Info: http://wiki.brainvoyager.com/BVQXtools
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% This Class creates, reads, and writes BrainVoyagerQX's
% native file formats as MATLAB ojbects.
%
% vmr = BVQXfile('new:vmr');   % create a new VMR
% vmr = BVQXfile('file.vmr');  % load file.vmr
% vmr = BVQXfile('*.vmr' [, titleornumber]);  % show file selector
%
% The storage is made in one global variable, bvqxcont, to keep only
% one copy of elements (full call-by-reference implementation), as of
% BVQXtools version v0.7b.
%
% To clear the storage, it is thus required to call one of the clearing
% methods:
%
% object.ClearObject;
% clearbvqxobjects({object, ...});
%
% Properties in the BVQX objects are then accessible like struct fields:
%
% dimx = vmr.DimX;
%
% Methods are equally accessible and, if available, overload properties.
% Some methods work for all (several) object / file types:
%
% bbox = vmr.BoundingBox;
% unused.ClearObject;
% copied = vmr.CopyObject;
% filename = vmr.FilenameOnDisk;
% filetype = vmr.Filetype;
% firstvolume = vtc.GetVolume(1);
% vmr.ReloadFromDisk;
% vmr.Save;
% vmr.SaveAs(newfilename);
% vmr.SaveAs('*.vmr' [, title]);
%
% Alternatively, you can use the functional form:
%
% Call(vmr, 'MethodName' [, arguments]);
%
% To obtain help on object methods issue the call
%
% object.Help    - or -
% object.Help('Method')
%
% Additionally, certain commands / functions can be executed via
%
% BVQXfile(0, command [, arguments]);
%
% where the following commands are supported
%
% BVQXfile(0, 'clearallobjects')
%     clear the object storage (like clear in Matlab for objects WS)
%
% BVQXfile(0, 'clearobj', h)
%     clear the objects with internal handle(s) h
%
% BVQXfile(0, 'config', globalsettingname [, globalsettingvalue]]);
% BVQXfile(0, 'config' [, type [, settingname [, settingvalue]]]);
%     e.g.
% BVQXfile(0, 'config', 'vmr', 'autov16', false|true);
%     make global class configuration (on types)
%
% BVQXfile(0, 'copyobject', h)
%     make a copy of object with internal handle h
%
% BVQXfile(0, 'extensions')
%     return supported extensions (struct with cell array fields)
%
% BVQXfile(0, 'formats')
%     return file formats (struct with fields bff, tff, extensions, magic)
%
% BVQXfile(0, 'isobject', var)
%     implementation of isBVQXfile function
%
% BVQXfile(0, 'magic')
%     return just the file magic array (1xN struct)
%
% BVQXfile(0, 'makeobject', obj_struct)
%     return the classified object (struct -> class constructor)
%
% BVQXfile(0, 'methods')
%     return a struct with 1x1 struct field for each file type and
%     sub fields for each method supported by this type
%
% BVQXfile(0, 'newcont', type)
%     return the resulting struct of NewFileCode of Xff files
%
% BVQXfile(0, 'object', h)
%     return the occording object with handle h (implements a check)
%
% BVQXfile(0, 'transiosize');
%     retrieve current transio size setting for all BFF (struct)
% BVQXfile(0, 'transiosize', tiostruct);
%     sets transiosize (see last transiosize command below)
% BVQXfile(0, 'transiosize', 'vtc');
%     retrieve current transio size setting for one BFF (see next command)
% BVQXfile(0, 'transiosize', 'vtc', 1048576 [, boolean:updatecache]);
%     this sets the minimum number of bytes before , instead of loading
%     the data in a property field, creating a @transio object
%
% BVQXfile(0, 'unwindstack');
%     perform garbage collection (remove objects with unmatching dbstack)
%
% BVQXfile(0, 'updatecache');
%     updates the cache.mat file (with current transiosize settings)
%
% BVQXfile(0, 'updatedisable' [, type]);
% BVQXfile(0, 'updateenable' [, type]);
% BVQXfile(0, 'updatestate' [, type [, truefalse]]);
% BVQXfile(0, 'updatestate' [, truefalsestruct]]);
%     disable/enable automatic object updating (on type field)
%     for "undatestate" call can work on a 1x1 struct with type fields
%
% Note: the default constructor (BVQXfile without arguments) produces
%       a bogus BVQXfile object with content fields
%       Extensions, Formats, Magic, Methods
%       containing the supported features of the class

% declare global and persistent variables
global bvqxclup;
global bvqxcont;
global bvqxfile_config;
global bvqxlast;
persistent bvqxfile_singleton;

% check for Class initialization -> do it if necessary
if isempty(bvqxfile_singleton) || ...
   ~isstruct(bvqxfile_singleton) || ...
   ~bvqxfile_singleton.is_initialized

    % initialize global storage
    %
    % the storage of all objects is handled in a global variable,
    % bvqxcont, as of version 0.7a40
    %
    % this variable is a struct with fields
    %  .C   - object's content
    %  .F   - associated filename
    %  .H   - handles of other objects, etc.
    %  .L   - lookup value (referential integrity check)
    %  .S   - fileformat specification
    %  .U   - unwind stack information
    %
    % references will be looked up by object's internal L field
    % vs. the content of global variable bvqxclup
    bvqxcont = cell2struct(cell(0, 0, 6), {'C', 'F', 'H', 'L', 'S', 'U'}, 3);

    % configuration override on cache creation
    ovrcfg = struct;
    
    % hdr.assumeflipped is used when the HDR is read to determine the
    % convention state if the file itself does not contain this
    ovrcfg.hdr = struct;
    ovrcfg.hdr.assumeflipped = true;

    % vmr.autov16 decides whether V16 files are automatically loaded/saved
    ovrcfg.vmr = struct;
    ovrcfg.vmr.autov16 = true;

    % initialize persistent struct
    bvqxfile_singleton = struct;
    bvqxfile_singleton.is_initialized = false;
    bvqxfile_singleton.mlversion = version;
    bvqxfile_singleton.mmversion = str2double(bvqxfile_singleton.mlversion(1));
    bvqxfile_singleton.version = 'v0.8a;9102301;20091023011153';
    
    % try to use cached info
    bvqxfile_singleton.use_info_cache = true;

    % on initialization, also init global struct
    bvqxfile_config = struct;
    
    % loaded objects of private functions
    bvqxfile_config.loadedobjs = struct;
    
    % reloadsame controls whether the same file (name) should be
    % opened again on BVQXfile(filename) calls or a handle to the
    % already open object should be returned instead
    bvqxfile_config.reloadsame = true;
    
    % settings are loaded from cache/BVQXfile.ini and control
    % some of the functions (please check the contents of this file
    % every now and then)
    try
        bsi = BVQXinifile([fileparts(mfilename('fullpath')) ...
            '/cache/BVQXfile.ini'], 'convert');
        bvqxfile_config.settings = bsi.GetComplete;
    catch
        bvqxfile_config.settings = [];
    end
    
    % type later contains one field (1x1 struct) per filetype
    bvqxfile_config.type = struct;
    
    % unwindstack controls the "pseudo scope" of objects
    % if set to true, BVQXfile will remove objects from the global
    % storage, once the function that created the object has
    % been completely run
    bvqxfile_config.unwindstack = true;
    
    % update later contains one field (1x1 logical) per filetype
    % if this field is set to true and a method with filename
    % OBJ_Update(obj, F, S, V) exists, the method is called after
    % calls to subsasgn(obj, S, V)
    bvqxfile_config.update = struct;
    
    % get base folder
    bvqxfile_singleton.base_folder = fileparts(mfilename('fullpath'));
    
    % try cache
    usecache = bvqxfile_singleton.use_info_cache;
    if usecache
        cachefile = ...
            [bvqxfile_singleton.base_folder filesep 'cache' filesep 'cache.mat'];
        if exist(cachefile, 'file') == 2
            try
                cachemat = load(cachefile);
                cache = cachemat.cache;
                if isfield(cache, 'file_formats') && ...
                    isfield(cache, 'ff_methods') && ...
                    isfield(cache, 'bvqxconfig') && ...
                    isfield(cache, 'version') && ...
                    strcmp(cache.version, bvqxfile_singleton.version)
                    bvqxfile_singleton.file_formats = cache.file_formats;
                    bvqxfile_singleton.ff_methods = cache.ff_methods;
                    bvqxfile_config.reloadsame = cache.bvqxconfig.reloadsame;
                    bvqxfile_config.type = cache.bvqxconfig.type;
                    bvqxfile_config.update = cache.bvqxconfig.update;
                else
                    usecache = false;
                end
                bvqxfile_singleton.cachefile = cachefile;
            catch
                usecache = false;
            end
        else
            usecache = false;
        end
    end
    
    % otherwise (or failed)
    if ~usecache

        % get file formats
        bvqxfile_singleton.file_formats = bvqxfile_readformats( ...
            [bvqxfile_singleton.base_folder filesep 'formats'] ...
        );
    
        % override configs
        orf = fieldnames(ovrcfg);
        for orfc = 1:numel(orf)
            orc = ovrcfg.(orf{orfc});
            orcf = fieldnames(orc);
            for orcfc = 1:numel(orcf)
                bvqxfile_config.type.(orf{orfc}).(orcf{orcfc}) = ...
                    orc.(orcf{orcfc});
            end
        end

        % get file format specific methods
        bvqxfile_singleton.ff_methods = ...
            bvqxfile_getmethods(bvqxfile_singleton.file_formats.extensions);
        
        % save ?
        if bvqxfile_singleton.use_info_cache
            cache = struct;
            cache.version = bvqxfile_singleton.version;
            cache.file_formats = bvqxfile_singleton.file_formats;
            cache.ff_methods = bvqxfile_singleton.ff_methods;
            cache.bvqxconfig.reloadsame = bvqxfile_config.reloadsame;
            cache.bvqxconfig.type = bvqxfile_config.type;
            cache.bvqxconfig.update = bvqxfile_config.update;
            if bvqxfile_singleton.mmversion < 7
                save(cachefile, 'cache');
            else
                save(cachefile, 'cache', '-v6');
            end
        end

    end

    % get extension and magic list
    bvqxfile_singleton.bff = bvqxfile_singleton.file_formats.bff;
    bvqxfile_singleton.tff = bvqxfile_singleton.file_formats.tff;
    bvqxfile_singleton.ext = bvqxfile_singleton.file_formats.extensions;
    bvqxfile_singleton.mag = bvqxfile_singleton.file_formats.magic;

    % say we are initialized
    bvqxfile_singleton.is_initialized = true;

    % fill main object
    bvqxclup = 1;
    bvqxcont(1) = struct( ...
        'C', struct( ...
            'Extensions', BVQXfile(0, 'extensions'), ...
            'Formats',    BVQXfile(0, 'formats'), ...
            'Magic',      BVQXfile(0, 'magic'), ...
            'Methods',    BVQXfile(0, 'methods')), ...
    	'F', 'BVQXfile:BVQXfile', ...
        'H', struct('BVQXfile', 0, 'CleanUp', {{}}), ...
        'L', 1, ...
    	'S', struct('Extensions', {{'ROOT'}}, 'FFTYPE', 'ROOT'), ...
        'U', {{ }} );
    bvqxcont(1).U(:) = [];
    
    % set last object
    bvqxlast = [0, -1];
end

% single char argument or char + double
if ...
    nargin > 0 && ...
    nargin < 3 && ...
    ischar(varargin{1}) && ...
   ~isempty(varargin{1}) && ...
    numel(varargin{1}) < 256 && ...
   (nargin < 2 || ...
    (isa(varargin{2}, 'double') && ...
     numel(varargin{2}) == 1) || ...
    (ischar(varargin{2}) && ...
     ~isempty(varargin{2})))
    
    % linearize filename
    filename = varargin{1}(:)';
    [fnparts{1:3}] = fileparts(filename);

    % available file extensions
    ext = bvqxfile_singleton.ext;
    exn = fieldnames(ext);

    % check for "*.???"
    if ~isempty(fnparts{2}) && ...
        any(fnparts{2} == '*')
        
        % how many files
        if nargin < 2 || ...
           ~isa(varargin{2}, 'double') || ...
            isnan(varargin{2}) || ...
            varargin{2} < 1
            numreq = 1;
        else
            numreq = floor(varargin{2});
        end
        if nargin > 1 && ...
            ischar(varargin{2})
            dtitle = varargin(2);
        else
            dtitle = cell(0, 1);
        end
        
        filename = bvqxfile_requestfile(numreq, filename, ext, ...
            bvqxfile_singleton.file_formats, dtitle{:});
        
        % any filename(s) returned
        if isempty(filename)
            varargout(1:nargout) = cell(1, nargout);
            return;
        elseif ischar(filename)
            filename = {filename};
        end
        nfiles = numel(filename);
        
        % create output object
        oo = BVQXfile(0, 'makeobject', struct('L', 1));
        ol = true(1, nfiles);
        if nfiles > 1
            oo(2:nfiles) = oo(1);
        end
        
        % try loading objects
        for oc = 1:nfiles
            try
                if bvqxfile_config.reloadsame
                    oo(oc) = BVQXfile(filename{oc});
                else
                    if ispc
                        namelup = strcmpi(filename{oc}, {bvqxcont(:).F});
                    else
                        namelup = strcmp(filename{oc}, {bvqxcont(:).F});
                    end
                    if ~isempty(namelup)
                        oo(oc) = BVQXfile(0, 'makeobject', ...
                            struct('L', bvqxclup(namelup(end))));
                        ol(oc) = false;
                    else
                        oo(oc) = BVQXfile(filename{oc});
                    end
                end
            catch
                error( ...
                    'BVQXfile:FileReadError', ...
                    'Error reading file ''%s'': %s.', ...
                    filename{oc}, lasterr ...
                );
            end
        end
        
        % return objects
        varargout{1} = oo;
        varargout{2} = ol;
        return;
    
    % patch extension only with "new:???"
    elseif numel(filename) < 5 && ...
       (exist(filename, 'file') ~= 2 || ...
        exist([filename '.m'], 'file') == 2)
        filename = ['new:' filename];
        
    end
    
    % checking for 'new:???'
    if length(filename) > 5 && ...
        strcmpi(filename(1:4), 'new:') && ...
       ~any(filename == '\' | filename == '/' | filename == '.') && ...
        isfield(ext, lower(filename(5:end)))

        % get spec
        fftype = lower(filename(5:end));
        ffspec = ext.(fftype);
        xfft = ffspec{1}(end-2:end);
        spec = bvqxfile_singleton.file_formats.(xfft)(ffspec{2});

        % no "new" code is available
        if isempty(spec.NewFileCode)
            error( ...
                'BVQXfile:IncompleteSpec', ...
                'For %s type files, no NewFileCode is available.', ...
                fftype ...
            );
        end;

        % make new object's lookup value
        nlup = rand(1, 1);
        while any(bvqxclup == nlup)
            nlup = rand(1, 1);
        end

        % get new object's content
        try
            bc = BVQXfile(0, 'newcont', fftype);
            if bvqxfile_config.unwindstack
                mst = mystack;
            else
                mst = {};
            end
            bvqxcont(end + 1) = struct( ...
                'C', bc, ...
                'F', '', ...
                'H', struct('BVQXfile', nlup, 'CleanUp', {{}}), ...
                'L', nlup, ...
                'S', spec, ...
                'U', {mst});
            bvqxclup(end + 1) = nlup;
        catch
            error( ...
                'BVQXfile:EvaluationError', ...
                'Couldn''t evaluate NewFileCode snippet for type %s.', ...
                fftype ...
            );
        end

        % build object
        varargout{1} = BVQXfile(0, 'makeobject', struct('L', nlup));
        varargout{2} = true;
        return;
    end

    % make absolute!
    [isabs{1:2}] = isabsolute(filename);
    filename = isabs{2};

    % get file name parts
    [fx{1:3}] = fileparts(filename);
    fx = fx{3};
    if ...
       ~isempty(fx) && ...
        fx(1) == '.'
        fx(1) = [];
    end

    % any extension based match
    exf = strcmpi(fx, exn);
    if any(exf)

        % get match and matching extension field name
        exf = find(exf);
        exn = exn{exf(1)};

        % look up in extensions
        if isfield(ext, exn)
            ffspec = ext.(exn);
            xfft = ffspec{1}(end-2:end);
            ff = bvqxfile_singleton.file_formats.(xfft)(ffspec{2});

        % or error !
        else
            error( ...
                'BVQXtools:BVQXfile:BadExtension', ...
                'Extension given, but not in BFF/TFF list: %s.', ...
                exn ...
            );
        end

        % match found
        exf = true;
    else

        % no match found
        exf = false;
    end

    % not yet identified, try magic
    if ~exf
        maf = false;
        try
            detmag = bvqxfile_detectmagic(filename, bvqxfile_singleton.mag);
        catch
            detmag = '';
        end
        if ~isempty(detmag)

            % either bff
            if isfield(ext, detmag)
                ffspec = ext.(detmag);
                xfft = ffspec{1}(end-2:end);
                ff = bvqxfile_singleton.file_formats.(xfft)(ffspec{2});

            % or error !
            else
                error( ...
                    'BVQXtools:BVQXfile:BadExtension', ...
                    'Magic found, but type not in BFF/TFF list: %s.', ...
                    detmag ...
                );
            end
            maf = true;
        end
    end

    if ~exf && ~maf
        error( ...
            'BVQXtools:BVQXfile:BadFileContent', ...
            'Unknown file type. Cannot read ''%s''.', ...
            filename ...
        );
    end

    % read file
    try
        switch lower(ff.FFTYPE)
            case {'bff'}
                ffcont = bffio(filename, ff);
            case {'tff'}
                ffcont = tffio(filename, ff);
            otherwise
                error( ...
                    'BVQXtools:BVQXfile:BadFFTYPE', ...
                    'FF type %s not supported yet.', ...
                    ff.FFTYPE ...
                );
        end
        if ispc
            filename = strrep(filename, '\', '/');
        end
        nlup = rand(1, 1);
        while any(bvqxclup == nlup)
            nlup = rand(1, 1);
        end
        if bvqxfile_config.unwindstack
            mst = mystack;
        else
            mst = {};
        end
        bvqxcont(end + 1) = struct( ...
            'C', ffcont, ...
            'F', filename, ...
            'H', struct('BVQXfile', nlup, 'CleanUp', {{}}), ...
            'L', nlup, ...
            'S', ff, ...
            'U', {mst});
        bvqxclup(end + 1) = nlup;
        varargout{1} = BVQXfile(0, 'makeobject', struct('L', nlup));
        varargout{2} = true;
        return;
    catch
        error( ...
            'BVQXtools:BVQXfile:XFFioFailed', ...
            'Error calling *ffio(...): ''%s''.', ...
            lasterr ...
        );
    end


% elseif ... other input argument combinations
elseif ...
    nargin == 2 && ...
    ischar(varargin{1}) && ...
   ~isempty(varargin{1}) && ...
    numel(varargin{2}) == 1 && ...
    isBVQXfile(varargin{2}, true)

    % try writing file
    try
        olup = (bvqxclup == varargin{2}.L);
        ostr = bvqxcont(olup);
        switch lower(ostr.S.FFTYPE)
            case {'bff'}
                bvqxcont(olup).C = bffio(varargin{1}, ostr.S, ostr.C);
            case {'tff'}
                [bvqxcont(olup).C, bvqxcont(olup).F] = ...
                    tffio(varargin{1}, ostr.S, ostr.C);
            otherwise
                error( ...
                    'BVQXtools:BVQXfile:BadFFTYPE', ...
                    'FF type %s not supported yet.', ...
                    ostr.S.FFTYPE ...
                );
        end
    catch
        error( ...
            'BVQXtools:BVQXfile:xFFioFailed', ...
            'Error calling ?ffio(...): ''%s''.', ...
            lasterr ...
        );
    end

% special cases for internal call
elseif nargin > 1 && ...
    isa(varargin{1}, 'double') && ...
    numel(varargin{1}) == 1 && ...
    varargin{1} == 0 && ...
    ischar(varargin{2})

    % what special case
    switch (lower(varargin{2}(:)'))
        
        % unwind stack
        case {'unwindstack'}
            
            % allow to change setting
            if nargin > 2 && ...
                islogical(varargin{3}) && ...
                numel(varargin{3}) == 1
                bvqxfile_config.unwindstack = varargin{3};
            end
            
            % doesn't work if not enabled
            if ~bvqxfile_config.unwindstack
                return;
            end
            
            % get current stack
            cst = mystack;

            % iterate over all objects
            uobjs = false(1, numel(bvqxcont));
            ost = {bvqxcont(:).U};
            for uc = 2:numel(uobjs)

                % continue if stack is empty
                if isempty(ost{uc})
                    continue;
                end
                
                % compare last entries
                if numel(cst) < numel(ost{uc}) || ...
                    any(~strcmp(ost{uc}, cst(end+1-numel(ost{uc}):end)))
                    uobjs(uc) = true;
                end
            end
            
            % cleanups of objects
            clu = {bvqxcont(uobjs).H};
            for c = 1:numel(clu)
                if isfield(clu{c}, 'CleanUp') && ...
                    iscell(clu{c}.CleanUp) && ...
                   ~isempty(clu{c}.CleanUp)
                    clus = clu{c}.CleanUp;
                    for cluc = 1:numel(clus)
                        if ischar(clus{cluc}) && ...
                           ~isempty(clus{cluc})
                            evalin('base', clus{cluc}(:)', '');
                        end
                    end
                end
            end

            % remove unwinded objects
            bvqxclup(uobjs) = [];
            bvqxcont(uobjs) = [];
            
            % return flag
            varargout{1} = bvqxfile_config.unwindstack;
            
        % check object validity
        case {'isobject'}
            
            % class check first
            if ~isa(varargin{3}, 'BVQXfile')
                varargout{1} = false;
                return;
            end
            hfile = varargin{3};
            
            % arguments
            if nargin > 3 && ...
                islogical(varargin{4}) && ...
                ~isempty(varargin{4}) && ...
                varargin{4}(1)
                if nargin > 4 && ...
                    ischar(varargin{5})
                    chtypest = varargin{5}(:)';
                else
                    chtypest = '';
                end
            else
                varargout{1} = true(size(hfile));
                return;
            end
                
            % validation ?
            isf = true(size(hfile));
            for oc = 1:numel(hfile)

                % L must lead to global storage
                if ~any(bvqxclup == hfile(oc).L)
                    isf(oc) = false;
                    continue;
                end

                % if that passed, check extension if given
                if ~isempty(chtypest) && ...
                   ~strcmpi(chtypest, bvqxcont(bvqxclup == hfile(oc).L).S.Extensions{1})
                    isf(oc) = false;
                end
            end
            varargout{1} = isf;

        % create object from struct
        case {'makeobject'}
            
            % check integrity
            if nargin > 2 && ...
                isstruct(varargin{3}) && ...
                length(fieldnames(varargin{3})) == 1 && ...
                all(strcmp(fieldnames(varargin{3}), {'L'}))
            
                % create object
                try
                    varargout{1} = class(varargin{3}, 'BVQXfile');
                catch
                    error( ...
                        'BVQXfile:BadStructForClass', ...
                        'Bad struct given, cannot create object.' ...
                    );
                end
            else
                error( ...
                    'BVQXfile:BadStructForClass', ...
                    'Bad struct given, cannot create object.' ...
                );
            end
            
        % evaluate NewFileCode
        case {'newcont'}
            if nargin < 3 || ...
               ~ischar(varargin{3}) || ...
                isempty(varargin{3}) || ...
               ~isfield(bvqxfile_singleton.ext, lower(varargin{3}(:)'))
                error( ...
                    'BVQXfile:BadArgument', ...
                    'Unknown filetype.' ...
                );
            end
            fftype = lower(varargin{3});
            ffspec = bvqxfile_singleton.ext.(fftype);
            xfft = ffspec{1}(end-2:end);
            spec = bvqxfile_singleton.file_formats.(xfft)(ffspec{2});
            bffcont = struct;
            newcode = spec.NewFileCode;
            if lower(spec.FFTYPE(1)) == 't'
                newcode = strrep(newcode, 'tffcont', 'bffcont');
            end
            try
                eval(newcode);
                varargout{1} = bffcont;
                return;
            catch
                error( ...
                    'BVQXfile:EvaluationError', ...
                    'Couldn''t evaluate NewFileCode snippet for type %s.', ...
                    fftype ...
                );
            end
            
        % remove one object from the array
        case {'clearobj'}
            if nargin > 2 && ...
                isa(varargin{3}, 'double') && ...
                all(varargin{3}(:) >= 0 & varargin{3}(:) <= 1)
            
                % remove
                bvqxfile_clear(varargin{3}(:)');
            end
            
        % copy object
        case {'copyobject'}
            if nargin > 2 && ...
                isa(varargin{3}, 'double') && ...
                numel(varargin{3}) == 1 && ...
                ~isnan(varargin{3}) && ...
                varargin{3} < 1 && ...
                any(bvqxclup == varargin{3})
                olup = find(bvqxclup == varargin{3});
                nlup = rand(1, 1);
                while any(bvqxclup == nlup)
                    nlup = rand(1, 1);
                end
                bvqxclup(end + 1) = nlup;
                bvqxcont(end + 1) = bvqxcont(olup(1));
                bvqxcont(end).F = '';
                bvqxcont(end).H = struct('BVQXfile', nlup, 'CleanUp', {{}});
                bvqxcont(end).L = nlup;
                if bvqxfile_config.unwindstack
                    bvqxcont(end).U = mystack;
                end
                varargout{1} = BVQXfile(0, 'makeobject', struct('L', nlup));
                varargout{2} = true;
            else
                error( ...
                    'BVQXfile:BadArgument', ...
                    'Bad argument or invalid object lookup.' ...
                );
            end
            
        % class (types) configuration
        case {'config'}
            if nargin < 3 || ...
               ~ischar(varargin{3}) || ...
              (~isfield(bvqxfile_config.type, varargin{3}(:)') && ...
               ~isfield(bvqxfile_config, varargin{3}(:)'))
                error( ...
                    'BVQXfile:BadArgument', ...
                    'Bad or missing argument in config call.' ...
                );
            end
            
            % global configuration
            if isfield(bvqxfile_config, varargin{3}(:)')
                
                % set
                if nargin > 3
                    bvqxfile_config.(varargin{3}(:)') = varargin{4};
                else
                    varargout{1} = bvqxfile_config.(varargin{3}(:)');
                end
                
            % type configuration
            else
                
                % get current config
                curcfg = bvqxfile_config.type.(varargin{3}(:)');
                
                % sub indexing
                if nargin > 3 && ...
                    ischar(varargin{4}) && ...
                   ~isempty(varargin{4}) && ...
                    isfield(curcfg, varargin{4}(:)')
                
                    % set
                    if nargin > 4
                        bvqxfile_config.type.(varargin{3}(:)').(varargin{4}(:)') = varargin{5};
                        
                    % get
                    else
                        varargout{1} = curcfg.(varargin{4}(:)');
                    end
                    
                % entire type config
                else
                    varargout{1} = curcfg;
                end
            end
            
        % transio size settings
        case {'transiosize'}
            
            % make per type setting
            if nargin > 3 && ...
                ischar(varargin{3}) && ...
                strcmp(makelabel(varargin{3}(:)'), varargin{3}(:)') && ...
                isfield(bvqxfile_singleton.ext, varargin{3}(:)') && ...
               ~isempty(strfind(bvqxfile_singleton.ext.(varargin{3}(:)'){1}, '.bff')) && ...
                isa(varargin{4}, 'double') && ...
                numel(varargin{4}) == 1 && ...
                ~isnan(varargin{4}) && ...
                varargin{4} > 4095
            
                % use a fixed size
                tsz = fix(varargin{4});
                fpos = bvqxfile_singleton.ext.(varargin{3}(:)'){2};
                bvqxfile_singleton.bff(fpos).TransIOSize = tsz;
                bvqxfile_singleton.file_formats.bff(fpos).TransIOSize = tsz;
                
                % store cache
                if nargin > 4 && ...
                    isa(varargin{5}, 'logical') && ...
                    numel(varargin{5}) == 1 && ...
                    varargin{5}
                    BVQXfile(0, 'updatecache');
                end
                
            % get per type setting
            elseif nargin > 2 && ...
                ischar(varargin{3}) && ...
                strcmp(makelabel(varargin{3}(:)'), varargin{3}(:)') && ...
                isfield(bvqxfile_singleton.ext, varargin{3}(:)') && ...
               ~isempty(strfind(bvqxfile_singleton.ext.(varargin{3}(:)'){1}, '.bff'))
                fpos = bvqxfile_singleton.ext.(varargin{3}(:)'){2};
                varargout{1} = bvqxfile_singleton.bff(fpos).TransIOSize;
                
            % set entire list of types
            elseif nargin > 2 && ...
                isstruct(varargin{3}) && ...
                numel(varargin{3}) == 1 && ...
                numel(fieldnames(varargin{3})) == numel(bvqxfile_singleton.bff)
            
                % get struct and check fieldnames against extensions
                tsz = varargin{3};
                tszf = fieldnames(tsz);
                for sc = 1:numel(bvqxfile_singleton.bff)
                    if ~strcmp(tszf{sc}, bvqxfile_singleton.bff(sc).Extensions{1}) || ...
                       ~isa(tsz.(tszf{sc}), 'double') || ...
                        numel(tsz.(tszf{sc})) ~= 1 || ...
                        isnan(tsz.(tszf{sc})) || ...
                        tsz.(tszf{sc}) < 4096
                        error( ...
                            'BVQXfile:BadArgument', ...
                            'Invalid transiosize structure.' ...
                        );
                    end
                end
                
                % make setting
                for sc = 1:numel(bvqxfile_singleton.bff)
                    bvqxfile_singleton.bff(sc).TransIOSize = tsz.(tszf{sc});
                    bvqxfile_singleton.file_formats.bff(sc).TransIOSize = tsz.(tszf{sc});
                end
                
                % store in cache
                if nargin > 3 && ...
                    isa(varargin{4}, 'logical') && ...
                    numel(varargin{4}) == 1 && ...
                    varargin{4}
                    BVQXfile(0, 'updatecache');
                end
                
            % set for all types
            elseif nargin > 2 && ...
                isa(varargin{3}, 'double') && ...
                numel(varargin{3}) == 1 && ...
                ~isnan(varargin{3}) && ...
                varargin{3} > 4095
                tsz = fix(varargin{3});
                
                % iterate over formats
                for sc = 1:numel(bvqxfile_singleton.bff)
                    bvqxfile_singleton.bff(sc).TransIOSize = tsz;
                    bvqxfile_singleton.file_formats.bff(sc).TransIOSize = tsz;
                end
                
                % store in cache
                if nargin > 3 && ...
                    isa(varargin{4}, 'logical') && ...
                    numel(varargin{4}) == 1 && ...
                    varargin{4}
                    BVQXfile(0, 'updatecache');
                end
                
            % get entire list of types
            elseif nargin == 2
                tsz = struct;
                for sc = 1:numel(bvqxfile_singleton.bff)
                    tsz.(bvqxfile_singleton.bff(sc).Extensions{1}) = ...
                        bvqxfile_singleton.bff(sc).TransIOSize;
                end
                varargout{1} = tsz;
            end
            
        % list of supported extensions / types
        case {'extensions'}
            varargout{1} = bvqxfile_singleton.ext;
            
        % format specifications
        case {'formats'}
            varargout{1} = bvqxfile_singleton.file_formats;
            
        % format specifications
        case {'help'}
            varargout{1} = root_Help(BVQXfile);
            
        % magic detection tokens
        case {'magic'}
            varargout{1} = bvqxfile_singleton.mag;
            
        % list of methods per type
        case {'methods'}
            varargout{1} = bvqxfile_singleton.ff_methods;
            
        % get object from lookup
        case {'object'}
            if nargin < 2 || ...
               ~isa(varargin{3}, 'double') || ...
                numel(varargin{3}) ~= 1 || ...
               ~any(bvqxclup == varargin{3})
                error( ...
                    'BVQXfile:LookupError', ...
                    'Object with given tag does not exist.' ...
                );
            end
            varargout{1} = BVQXfile(0, 'makeobject', struct('L', varargin{3}));
            varargout{2} = false;
            
        % list of objects (copy of global struct)
        case {'objects'}
            varargout{1} = bvqxcont;
            
        % clear storage completely
        case {'clearallobjects'}
            
            % simply remake internal arrays
            bvqxfile_singleton(1) = [];
            BVQXfile;

        % update cache
        case {'updatecache'}
            
            % only works if cachefile set and enabled
            if isfield(bvqxfile_singleton, 'cachefile') && ...
                bvqxfile_singleton.use_info_cache
            
                % compile cache
                cache = struct;
                cache.version = bvqxfile_singleton.version;
                cache.file_formats = bvqxfile_singleton.file_formats;
                cache.ff_methods = bvqxfile_singleton.ff_methods;
                cache.bvqxconfig.type = bvqxfile_config.type;
                cache.bvqxconfig.update = bvqxfile_config.update;
                
                % save according to version
                if bvqxfile_singleton.mmversion < 7
                    save(bvqxfile_singleton.cachefile, 'cache');
                else
                    save(bvqxfile_singleton.cachefile, 'cache', '-v6');
                end
            end
            
        % disable obj_Update calls
        case {'updatedisable'}
            
            % for one type
            if nargin > 2 && ...
               ischar(varargin{3}) && ...
               isfield(bvqxfile_config.update, lower(varargin{3}(:)'))
                bvqxfile_config.update.(lower(varargin{3}(:)')) = false;
                
            % for all types
            elseif nargin == 2
                ftn = fieldnames(bvqxfile_config.update);
                for ftc = 1:numel(ftn)
                    bvqxfile_config.update.(ftn{ftc}) = false;
                end
            end
            
        % enable obj_Update calls
        case {'updateenable'}
            
            % for one type
            if nargin > 2 && ...
                ischar(varargin{3}) && ...
                isfield(bvqxfile_config.update, lower(varargin{3}(:)'))
                bvqxfile_config.update.(lower(varargin{3}(:)')) = true;
                
            % for all types
            elseif nargin == 2
                ftn = fieldnames(bvqxfile_config.update);
                for ftc = 1:numel(ftn)
                    bvqxfile_config.update.(ftn{ftc}) = true;
                end
            end
            
        % state of update flag
        case {'updatestate'}
            
            % for one type
            if nargin > 2 && ...
                ischar(varargin{3}) && ...
                isfield(bvqxfile_config.update, lower(varargin{3}(:)'))
            
                % set flag
                if nargin > 3 && ...
                    islogical(varargin{4}) && ...
                   ~isempty(varargin{4})
                    bvqxfile_config.update.(lower(varargin{3}(:)')) = varargin{4}(1);
                
                % get flag
                else
                    varargout{1} = bvqxfile_config.update.(lower(varargin{3}(:)'));
                end
           
           
            % for all types
            else
                
                % set flags
                if nargin > 2 && ...
                    isstruct(varargin{3}) && ...
                    numel(varargin{3}) == 1 && ...
                    numel(fieldnames(varargin{3})) == numel(fieldnames(bvqxfile_config.update)) && ...
                    all(strcmp(fieldnames(varargin{3}), fieldnames(bvqxfile_config.update)))
                
                    % check struct
                    sf = fieldnames(varargin{3});
                    for sfc = 1:numel(sf)
                        if ~islogical(varargin{3}.(sf{sfc})) || ...
                            numel(varargin{3}.(sf{sfc})) ~= 1
                            error( ...
                                'BVQXfile:BadArgument', ...
                                'Invalid updatestate argument.' ...
                            );
                        end
                    end
                    
                    % set struct
                    bvqxfile_config.update = varargin{3};
                
                % get flags
                else
                    
                    % pass out update struct
                    varargout{1} = bvqxfile_config.update;
                end
            end
            
        % bail out on study commands
        otherwise
            error( ...
                'BVQXtools:BVQXfile:BadSpecialArgument', ...
                'Invalid special argument given.' ...
            );
    end

% just a lookup value
elseif nargin == 1 && ...
    isa(varargin{1}, 'double') && ...
    numel(varargin{1}) == 1

    % make sure it's given as "not loaded"
    if nargout > 1
        varargout{2} = false;
    end

    % return object
    if any(bvqxclup == varargin{1})

        % looked up
        varargout{1} = BVQXfile(0, 'makeobject', struct('L', varargin{1}));
        
    % or
    elseif varargin{1} >=1 && ...
        varargin{1} <= numel(bvqxclup)
        
        % numbered entry
        varargout{1} = BVQXfile(0, 'makeobject', ...
            struct('L', bvqxclup(round(varargin{1}))));
        
    % if not found
    else
        error( ...
            'BVQXfile:BadLookup', ...
            'Invalid lookup for handle syntax.' ...
        );
    end

% list of filenames
elseif nargin > 0 && ...
    iscell(varargin{1}) && ...
   ~isempty(varargin{1})

    % try to load objects
    varargout{1} = cell(size(varargin{1}));
    for cc = 1:numel(varargin{1})
        if ischar(varargin{1}{cc}) && ...
           ~isempty(varargin{1}{cc})
            try
                if nargin > 1
                    varargout{1}{cc} = BVQXfile(varargin{1}{cc}, varargin{2:end});
                else
                    varargout{1}{cc} = BVQXfile(varargin{1}{cc});
                end
            catch
                clearbvqxobjects(varargout{1});
                rethrow(lasterror);
            end
        end
    end
    
% make case for NULL arguments (return ROOT if needed)
elseif nargin == 0
    if nargout > 0
        varargout{1} = BVQXfile(0, 'makeobject', struct('L', 1));
    end

% else
else
    if nargin == 1 && ...
        ischar(varargin{1})
        error( ...
            'BVQXtools:BVQXfile:BadArgument', ...
            'File not found.' ...
        );
    else
        error( ...
            'BVQXtools:BVQXfile:BadArgument', ...
            'Bad argument combination or file not writable.' ...
        );
    end

% end of argument test if
end



% internal function for stack unwinding
function mst = mystack
    
    % persistent config
    persistent msc;
    if isempty(msc)
        mlv = version;
        msc = (str2double(mlv(1)) < 7);
    end
    
    % which version
    if msc
        mst = dbstack;
        mst = {mst.name};
    else
        mst = dbstack('-completenames');
        mst = {mst.file};
    end
    
    % remove BVQXfile from stack
    mst(1:2) = [];
    rst = true(1, numel(mst));
    for rc = 1:numel(rst)
        if ~isempty(strfind(mst{rc}, '@BVQXfile'))
            rst(rc) = false;
        end
    end
    mst = mst(rst);
% end of function mst = mystack
